﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Pifagor.IG;
using Pifagor.Exceptions;

namespace Pifagor.Runtime
{
	static class ComputableFactory
	{
		internal static IComputableValue CreateComputable(ExecutionContext context, IGNode node)
		{
			switch (node.Type)
			{
				case IGNodeType.Interpretation:
					return CreateInterpretationComputable(context, node);

				case IGNodeType.Constant:
					return new Value(context, node.ConstantValue);

				case IGNodeType.DataList:
					return new DataList(context, node);

				case IGNodeType.ParList:
					return new ParList(context, node);

				default:
					throw new RuntimeException("Unrecognized type of root node");
			}
		}

		static IComputableValue CreateInterpretationComputable(ExecutionContext context, IGNode node)
		{
			if (node.Links.Length != 2)
				throw new FormatReadException("Interpretation must have two links");

			var argumentNode = context.IG.Nodes[node.Links[0].Id];
			var functionNode = context.IG.Nodes[node.Links[1].Id];

			var argumentValueComputable = ComputableFactory.CreateComputable(context, argumentNode);
			var functionValueComputable = ComputableFactory.CreateComputable(context, functionNode);

			if (argumentNode.Type == IGNodeType.ParList)
			{
				ParList argumentParList = argumentValueComputable.Compute().ParListValue;
				if (functionNode.Type != IGNodeType.ParList)
				{
					var interpretationsParListItems =
						argumentParList.Items.Select(argumentItem =>
							new Interpretation(context, argumentItem, functionValueComputable)).ToArray();
					return new ParList(context, interpretationsParListItems);
				}
				else
				{
					ParList functionParList = functionValueComputable.Compute().ParListValue;
					var newInterpretationsParListItems =
						(
							from argumentItem in argumentParList.Items
							from functionItem in functionParList.Items
							select new Interpretation(context, argumentItem, functionItem)
						).ToArray();

					return new ParList(context, newInterpretationsParListItems);
				}
			}
			else
			{
				if (functionNode.Type != IGNodeType.ParList)
				{
					return new Interpretation(context, argumentValueComputable, functionValueComputable);
				}
				else
				{
					ParList functionParList = functionValueComputable.Compute().ParListValue;
					return new ParList(context,
						functionParList.Items.Select(functionItem =>
							new Interpretation(context, argumentValueComputable, functionItem)).ToArray());
				}
			}
		}
	}
}
